﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using SNFEGame.Menus;
using SNFEGame.Util;
using SNFEGame.Visual;

namespace SNFEGame.States
{
    class GameState : BaseState
    {
        private const float TRANSITION_ALPHA = 0.4f;

        private Menu pauseMenu;
        private bool paused = false;
        private int levelNumber = 0;
        private bool p1TouchingEndFlag;
        private bool p2TouchingEndFlag;

        private bool gameOver = false;
        private float waitTime = 0f;
        private const float TOTAL_WAIT_TIME = 1f;

        private bool pressedDown = false;

        private Character character1, character2;

        private List<Sprite> drawSprites, drawSprites2, updateSprites, decorativeSprites;
        private List<Vector2> backgrounds;

        private bool firstWorldActive, transition;

        SoundEffectInstance jumpingAudio, runningAudio;

        private Matrix cameraTransform;
        private Vector2 cameraPos;
        private ContentManager content;

        private Color character1Color, character2Color;
        private Color character1ColorA, character2ColorA;
        private Viewport defaultViewport;

        public GameState(ContentManager content, Viewport defaultViewport)
        {

            this.content = content;
            this.defaultViewport = defaultViewport;
            Initialize();
        }

        protected override void Initialize()
        {
            firstWorldActive = true;
            transition = true;

            jumpingAudio = Storage.jump.CreateInstance(); ;
            runningAudio = Storage.run.CreateInstance(); ;

            MediaPlayer.Play(Storage.music);
            updateSprites = new List<Sprite>();
            drawSprites = new List<Sprite>();
            drawSprites2 = new List<Sprite>();
            decorativeSprites = new List<Sprite>();
            backgrounds = new List<Vector2>();


            backgrounds.Add(new Vector2(0, 0));

            // Menu initialization
            List<MenuItem> items = new List<MenuItem>();
            items.Add(new MenuItem("Resume Game", delegate()
            {
                paused = !paused;
            }));
            items.Add(new MenuItem("Restart Game", delegate()
            {
                MainGame.GoToGame();
            }));
            items.Add(new MenuItem("Exit to Main Menu", delegate()
            {
                MainGame.GoToMainMenu();
            }));
            items.Add(new MenuItem("Exit Game", delegate()
            {
                MainGame.CloseApplication();
            }));

            pauseMenu = new Menu(items, Storage.GetFont("Large"));

            Console.WriteLine("Maingame.width / 2: " + MainGame.Width / 2f);

            LevelQueueing();

            cameraPos.X = -100;
            cameraTransform = Matrix.CreateTranslation(cameraPos.X, cameraPos.Y, 0.0f) * Matrix.CreateTranslation(defaultViewport.Width * 0.5f, defaultViewport.Height * 0.5f, 0);
        }

        public override void Update(float dT)
        {
            if (gameOver)
            {
                waitTime += dT;
                if (waitTime >= TOTAL_WAIT_TIME)
                    MainGame.GoToMainMenu();
            }
            else
            {
                if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape))
                {
                    if (!pressedDown)
                    {
                        paused = !paused;

                        if (paused)
                            pauseMenu.Show();
                    }

                    pressedDown = true;
                }
                else
                {
                    pressedDown = false;
                }

                #region First/Second world checking code
                currentChar.color = currentActiveColor;
                alphaChar.color = currentInactiveAlphaColor;

                foreach (Sprite x in currentChar.sprites)
                {
                    x.color = Color.White;

                    if (currentChar.isTouching(x))
                    {
                        if(!alphaChar.sprites.Contains(target(x)))
                            alphaChar.sprites.Insert(alphaChar.sprites.Count - 2, target(x));
                    }
                    else
                    {

                        if (alphaChar.sprites.Contains(target(x)) && !alphaChar.isTouching(target(x)))
                            alphaChar.sprites.Remove(target(x));

                    }
                }
                foreach (Sprite x in alphaChar.sprites)
                {
                    if (!alphaChar.isTouching(x) && !currentChar.isTouching(x))
                        x.color = currentInactiveAlphaColor;
                    else
                        if(alphaChar.isTouching(x))
                            x.color = currentInactiveColor;


                }

                #endregion

                if (paused)
                    pauseMenu.Update(dT);
                else
                {
                    #region Game Key Input
                    if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Space))
                    {
                        if (transition == true)
                        {

                            transition = false;

                            firstWorldActive = !firstWorldActive;
                            if (firstWorldActive)
                                character2.setMovement(Direction.NONE);
                            else
                                character1.setMovement(Direction.NONE);
                        }
                    }

                    if (Keyboard.GetState(PlayerIndex.One).IsKeyUp(Keys.Space))
                    {
                        transition = true;
                    }
                    #endregion

                    #region Game Update Code
                    if (!character1.dead && !character2.dead)
                    {
                        ///Checks if the up arrow key is pressed
                        if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Up))
                        {
                            ///Makes the character jump, needs the code to deal with double-jump and how/when he can jump when falling
                            currentChar.jump(dT);

                            if (jumpingAudio.State == SoundState.Stopped && currentChar.justJumped == false)
                                jumpingAudio.Play();
                        }
                        else
                        {
                            //He isn't jumping? That means he just jumped! This makes sense, shut up (it actually does since the player can only have 2 states, justJumped and did not justJump ...ed.)
                            currentChar.justJumped = true;
                        }
                        ///Checks if the left arrow key is pressed
                        if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Left))
                        {
                            if (runningAudio.State == SoundState.Stopped && currentChar.isStanding)
                                runningAudio.Play();
                            else if (runningAudio.State == SoundState.Playing && !currentChar.isStanding)
                                runningAudio.Stop();
                            ///Sets the character's direction to the left
                            currentChar.setMovement(Direction.LEFT);

                        }
                        ///Checks if the right arrow key is pressed
                        else if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Right))
                        {

                            if (runningAudio.State == SoundState.Stopped && currentChar.isStanding)
                                runningAudio.Play();
                            else if (runningAudio.State == SoundState.Playing && !currentChar.isStanding)
                                runningAudio.Stop();
                            ///Sets the character's direction to the right
                            currentChar.setMovement(Direction.RIGHT);
                        }
                        ///For when a movement key is NOT pressed
                        else
                        {
                            if (runningAudio.State == SoundState.Playing)
                                runningAudio.Stop();
                            ///Stops adding to the velocity

                            currentChar.setMovement(Direction.NONE);

                            //character2.setMovement(Direction.NONE);
                        }

                        if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.M))
                        {
                            if (MediaPlayer.State == MediaState.Playing)
                                MediaPlayer.Pause();
                            else
                                MediaPlayer.Resume();


                        }

                    }
                    else
                    {
                        gameOver = true;
                    }

                    //Update the camera with the current location.
                    cameraTransform = Matrix.CreateTranslation(cameraPos.X, cameraPos.Y, 0.0f) * Matrix.CreateTranslation(defaultViewport.Width * 0.5f, defaultViewport.Height * 0.5f, 0);

                    /// run checkCollision() - runs onCollision methods
                    ///checkCollision();

                    /// run each sprite's update methods
                    foreach (Sprite updateMeBaby in updateSprites)
                        updateMeBaby.Update(dT);
                    if (firstWorldActive)
                        foreach (Sprite updateMeBaby in drawSprites)
                            updateMeBaby.Update(dT);
                    else
                        foreach (Sprite updateMeBaby in drawSprites2)
                            updateMeBaby.Update(dT);

                    #endregion
                }

                if (character1.isTouching(Storage.endFlag1))
                    p1TouchingEndFlag = true;
                if (character2.isTouching(Storage.endFlag2))
                    p2TouchingEndFlag = true;
                if (p1TouchingEndFlag && p2TouchingEndFlag)
                    LevelQueueing();
            }
        }

        public Character currentChar
        {
            get
            {
                if (firstWorldActive)
                    return character1;
                return character2;
            }
        }

        public Sprite target(Sprite x)
        {
            if (x is LinkedBlock)
                return (x as LinkedBlock).partner;
            else
                return x;
        }

        public Character alphaChar
        {
            get
            {
                if (firstWorldActive)
                    return character2;
                return character1;
            }
        }

        public Color currentActiveColor
        {
            get
            {
                if (firstWorldActive)
                    return character1Color;
                return character2Color;
            }


        }

        public Color currentAlphaColor
        {
            get
            {
                if (firstWorldActive)
                    return character1ColorA;
                return character2ColorA;
            }


        }

        public Color currentInactiveColor
        {
            get
            {
                if (firstWorldActive)
                    return character2Color;
                return character1Color;
            }


        }

        public Color currentInactiveAlphaColor
        {
            get
            {
                if (firstWorldActive)
                    return character2ColorA;
                return character1ColorA;
            }

        }

        public override void Draw(SpriteBatch spriteBatch)
        {
            #region Draw background

            spriteBatch.Begin();
            //spriteBatch.Draw(Storage.sky, Vector2.Zero, Color.White);
            spriteBatch.End();
            #endregion

            #region Actual draw code
            spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default,
                     RasterizerState.CullCounterClockwise, null, cameraTransform);
            foreach (Sprite drawMeBaby in decorativeSprites)
            {
                drawMeBaby.Draw(spriteBatch);
            }
            
            //Draw all the backgrounds.
            //foreach (Vector2 pos in backgrounds)
            //    spriteBatch.Draw(Storage.rocks, pos, Color.White);

            //If we're 2/3rds through our current background, add a new one to the array.
            if (currentChar.position.X - currentChar.sprites[0].position.X > backgrounds[backgrounds.Count - 1].X + Storage.rocks.Width * 2 / 3 || alphaChar.position.X - alphaChar.sprites[0].position.X > backgrounds[backgrounds.Count - 1].X + Storage.rocks.Width * 2 / 3)
                backgrounds.Add(new Vector2(backgrounds[backgrounds.Count - 1].X + Storage.rocks.Width, 0));

            bool firstPass = true;
            foreach (Sprite drawMeBaby in drawSprites)
            {
                if (firstPass)
                    firstPass = false;
                else
                    drawMeBaby.Draw(spriteBatch);
            }
            firstPass = true;
            foreach (Sprite drawMeBaby in drawSprites2)
            {
                if (firstPass)
                    firstPass = false;
                else
                drawMeBaby.Draw(spriteBatch);
            }
            foreach (Sprite drawMeBaby in updateSprites)
                drawMeBaby.Draw(spriteBatch);
            spriteBatch.End();
            #endregion

            cameraPos = -currentChar.position;

            if (paused)
                pauseMenu.Draw(spriteBatch);
            else if (gameOver)
            {
                spriteBatch.Begin();
                spriteBatch.Draw(Storage.whiteDot, new Rectangle(0, 0, MainGame.Width, MainGame.Height), Color.Black * .5f);

                SpriteFont large = Storage.GetFont("Large");
                spriteBatch.DrawString(large, "Game Over",
                    new Vector2(MainGame.Width / 2 - large.MeasureString("Game Over").X / 2, MainGame.Height / 2 - large.MeasureString("Game Over").Y / 2),
                    Color.White);
                spriteBatch.End();
            }
        }

        public void ToggleBoundsDrawn()
        {
            foreach (Sprite s in drawSprites)
                s.DrawingBounds = !s.DrawingBounds;
            foreach (Sprite s in drawSprites2)
                s.DrawingBounds = !s.DrawingBounds;
            foreach (Sprite s in updateSprites)
                s.DrawingBounds = !s.DrawingBounds;
        }

        public void LevelQueueing()
        {
            decorativeSprites.Clear();
            drawSprites.Clear();
            drawSprites2.Clear();
            updateSprites.Clear();

            string levelDeco;
            string level1;
            string level2;
            levelNumber++;
            if (levelNumber == 1)
            {
                levelDeco = "Xml/tutorialDeco";
                level1 = "Xml/tutorial1";
                level2 = "Xml/tutorial2";
            }
            else if (levelNumber == 2)
            {
                levelDeco = "Xml/newTutorialDeco";
                level1 = "Xml/newTutorial1";
                level2 = "Xml/newTutorial2";
            }
            else if (levelNumber == 3)
            {
                levelDeco = "Xml/AADeco";
                level1 = "Xml/AA1";
                level2 = "Xml/AA2";
            }
            else
            {
                levelDeco = "Xml/tutorialDeco";
                level1 = "Xml/tutorial1";
                level2 = "Xml/tutorial2";
            }
            decorativeSprites = Storage.readLevelFromXML(content, levelDeco);
            drawSprites = Storage.readLevelFromXML(content, level1);
            drawSprites2 = Storage.readLevelFromXML(content, level2);
            foreach (Sprite x in drawSprites2)
                x.color = x.color * TRANSITION_ALPHA;

            character1 = new Character(drawSprites, drawSprites[0].position - new Vector2(32, 100f));
            drawSprites[0].position = new Vector2(-1500f, -1500f);

            Console.WriteLine("Maingame.width / 2: " + MainGame.Width / 2f + "\nx of character: " + drawSprites[0].position.X);
            character1.reachedEnd = false;
            character1Color = new Color(215, 215, 255, 255);
            character1ColorA = character1Color * TRANSITION_ALPHA;
            character1.color = character1Color;
            character2 = new Character(drawSprites2, drawSprites2[0].position - new Vector2(32, 100f));
            drawSprites2[0].position = new Vector2(-1500f, -1500f);
            character2Color = new Color(255, 255, 215, 255);
            character2ColorA = character2Color * TRANSITION_ALPHA;
            character2.color = character2ColorA;

            updateSprites.Add(character1);
            updateSprites.Add(character2);

            p1TouchingEndFlag = false;
            p2TouchingEndFlag = false;
        }
    }
}
